package com.shen.nettysocketchat.utils

import android.os.Handler
import com.shen.nettysocketchat.utils.CThreadPoolExecutor
import com.shen.nettysocketchat.utils.CThreadPoolExecutor.CThreadFactory
import com.shen.nettysocketchat.utils.CThreadPoolExecutor.CHandlerException
import android.os.Looper
import android.util.Log
import java.lang.Exception
import java.lang.StringBuilder
import java.util.HashMap
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicInteger

/**
 *
 * @ProjectName:     BoChat
 *
 * @ClassName:       CThreadPoolExecutor.java
 *
 * @PackageName:     com.bochat.app.utils
 * **
 *
 * @Description:     自定义固定大小的线程池
 * 每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。
 * 线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。
 *
 *
 * 合理利用线程池能够带来三个好处：
 * 第一：降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
 * 第二：提高响应速度。当任务到达时，任务可以不需要等到线程创建就能立即执行。
 * 第三：提高线程的可管理性。线程是稀缺资源，如果无限制的创建，不仅会消耗系统资源，还会降低系统的稳定性，使用线程池可以进行统一的分配，调优和监控。
 * 我们可以通过ThreadPoolExecutor来创建一个线程池：
 * new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);
 *
 *
 * corePoolSize（线程池的基本大小）：
 * 当提交一个任务到线程池时，线程池会创建一个线程来执行任务，
 * 即使其他空闲的基本线程能够执行新任务也会创建线程，等到需要执行的任务数大于线程池基本大小时就不再创建。
 * 如果调用了线程池的prestartAllCoreThreads方法，线程池会提前创建并启动所有基本线程。
 *
 *
 * runnableTaskQueue（任务队列）：用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。
 * ArrayBlockingQueue：是一个基于数组结构的有界阻塞队列，此队列按 FIFO（先进先出）原则对元素进行排序。
 *
 *
 * LinkedBlockingQueue：一个基于链表结构的阻塞队列，此队列按FIFO （先进先出） 排序元素，吞吐量通常要高于ArrayBlockingQueue。
 * 静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
 *
 *
 * SynchronousQueue：一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作，否则插入操作一直处于阻塞状态，
 * 吞吐量通常要高于LinkedBlockingQueue，静态工厂方法Executors.newCachedThreadPool使用了这个队列。
 *
 *
 * PriorityBlockingQueue：一个具有优先级的无限阻塞队列。
 *
 *
 * maximumPoolSize（线程池最大大小）：
 * 线程池允许创建的最大线程数。如果队列满了，并且已创建的线程数小于最大线程数，则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
 *
 *
 * ThreadFactory：
 * 用于设置创建线程的工厂，可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
 *
 *
 * RejectedExecutionHandler（饱和策略）：当队列和线程池都满了，说明线程池处于饱和状态，那么必须采取一种策略处理提交的新任务。
 * 这个策略默认情况下是AbortPolicy，表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
 * AbortPolicy：直接抛出异常。
 * CallerRunsPolicy：只用调用者所在线程来运行任务。
 * DiscardOldestPolicy：丢弃队列里最近的一个任务，并执行当前任务。
 * DiscardPolicy：不处理，丢弃掉。
 * 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
 *
 *
 * keepAliveTime（线程活动保持时间）：线程池的工作线程空闲后，保持存活的时间。
 * 所以如果任务很多，并且每个任务执行的时间比较短，可以调大这个时间，提高线程的利用率。
 *
 *
 * TimeUnit（线程活动保持时间的单位）：可选的单位有天（DAYS），小时（HOURS），分钟（MINUTES），
 * 毫秒(MILLISECONDS)，微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
 ** *
 *
 * @author:          FreddyChen
 *
 * @date:            2019/2/3 15:35
 *
 * @email:           chenshichao@outlook.com
 *
 * @see http://www.infoq.com/cn/articles/java-threadPool
 */
object CThreadPoolExecutor {
    private val TAG = CThreadPoolExecutor::class.java.simpleName
    private val CPU_COUNT = Runtime.getRuntime().availableProcessors() // CPU个数

    //    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;// 线程池中核心线程的数量
    //    private static final int MAXIMUM_POOL_SIZE = 2 * CPU_COUNT + 1;// 线程池中最大线程数量
    private val CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)) // 线程池中核心线程的数量
    private val MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1 // 线程池中最大线程数量
    private const val KEEP_ALIVE_TIME =
        30L // 非核心线程的超时时长，当系统中非核心线程闲置时间超过keepAliveTime之后，则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true，则该参数也表示核心线程的超时时长
    private const val WAIT_COUNT = 128 // 最多排队个数，这里控制线程创建的频率
    private var pool = createThreadPoolExecutor()


    private var mainThread: Thread? = null
    private var mainHandler: Handler? = null

    init {
        val mainLooper = Looper.getMainLooper()
        mainThread = mainLooper.thread
        mainHandler = Handler(mainLooper)
    }


    private fun createThreadPoolExecutor(): ThreadPoolExecutor? {
        if (pool == null) {
            pool = ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                LinkedBlockingQueue(WAIT_COUNT),
                CThreadFactory("CThreadPool", Thread.NORM_PRIORITY - 2),
                CHandlerException()
            )
        }
        return pool
    }

    private val jobsForUI = Executors.newFixedThreadPool(
        CORE_POOL_SIZE, CThreadFactory("CJobsForUI", Thread.NORM_PRIORITY - 1)
    )

    /**
     * 启动一个消耗线程，常驻后台
     *
     * @param r
     */
    fun startConsumer(r: Runnable, name: String) {
        runInBackground { CThreadFactory(name, Thread.NORM_PRIORITY - 3).newThread(r).start() }
    }

    /**
     * 提交到其他线程去跑，需要取数据的时候会等待任务完成再继续
     *
     * @param task
     * @return
     */
    fun <T> submitTask(task: Callable<T>?): Future<T> {
        return jobsForUI.submit(task)
    }

    /**
     * 强制清理任务
     *
     * @param task
     * @return
     */
    fun <T> cancelTask(task: Future<T>?) {
        task?.cancel(true)
    }

    /**
     * 从 Future 中获取值，如果发生异常，打日志
     *
     * @param future
     * @param tag
     * @param name
     * @return
     */
    fun <T> getFromTask(future: Future<T>, tag: String?, name: String?): T? {
        try {
            return future.get()
        } catch (e: Exception) {
            Log.e(tag, (if (name != null) "$name: " else "") + e.toString())
        }
        return null
    }

    fun runInBackground(runnable: Runnable?) {
        if (pool == null) {
            createThreadPoolExecutor()
        }
        pool!!.execute(runnable)
        //        Future future = pool.submit(runnable);
        //        try {
        //            future.get();
        //        } catch (InterruptedException e) {
        //            e.printStackTrace();
        //        } catch (ExecutionException e) {
        //            e.printStackTrace();
        //        }
    }

    val isOnMainThread: Boolean
        get() = mainThread === Thread.currentThread()

    fun runOnMainThread(r: Runnable) {
        if (isOnMainThread) {
            r.run()
        } else {
            mainHandler!!.post(r)
        }
    }

    fun runOnMainThread(r: Runnable, delayMillis: Long) {
        if (delayMillis <= 0) {
            runOnMainThread(r)
        } else {
            mainHandler!!.postDelayed(r, delayMillis)
        }
    }

    // 用于记录后台等待的Runnable，第一个参数外面的Runnable，第二个参数是等待中的Runnable
    private val mapToMainHandler = HashMap<Runnable, Runnable>()
    fun runInBackground(runnable: Runnable, delayMillis: Long) {
        if (delayMillis <= 0) {
            runInBackground(runnable)
        } else {
            val mainRunnable = Runnable {
                mapToMainHandler.remove(runnable)
                pool!!.execute(runnable)
            }
            mapToMainHandler[runnable] = mainRunnable
            mainHandler!!.postDelayed(mainRunnable, delayMillis)
        }
    }

    /**
     * 对runOnMainThread的，移除Runnable
     *
     * @param r
     */
    fun removeCallbackOnMainThread(r: Runnable?) {
        mainHandler!!.removeCallbacks(r!!)
    }

    fun removeCallbackInBackground(runnable: Runnable) {
        val mainRunnable = mapToMainHandler[runnable]
        if (mainRunnable != null) {
            mainHandler!!.removeCallbacks(mainRunnable)
        }
    }

    fun logStatus() {
        val sb = StringBuilder()
        sb.append("getActiveCount")
        sb.append(pool!!.activeCount)
        sb.append("\ngetTaskCount")
        sb.append(pool!!.taskCount)
        sb.append("\ngetCompletedTaskCount")
        sb.append(pool!!.completedTaskCount)
        Log.d(TAG, sb.toString())
    }

    fun logAllThreadStackTrace(): StringBuilder {
        val builder = StringBuilder()
        val liveThreads = Thread.getAllStackTraces()
        val i: Iterator<Thread> = liveThreads.keys.iterator()
        while (i.hasNext()) {
            val key = i.next()
            builder.append("Thread ").append(key.name)
                .append("\n")
            val trace = liveThreads[key]!!
            for (j in trace.indices) {
                builder.append("\tat ").append(trace[j]).append("\n")
            }
        }
        return builder
    }

    @JvmStatic
    fun main(args: Array<String>) {
        for (i in 0..9999) {
            println("index=$i")
            runInBackground { println("正在运行第[" + (i + 1) + "]个线程.") }
            try {
                Thread.sleep(10)
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
        }
    }

    class CThreadFactory : ThreadFactory {
        private val counter = AtomicInteger(1)
        private var prefix = ""
        private var priority = Thread.NORM_PRIORITY

        constructor(prefix: String, priority: Int) {
            this.prefix = prefix
            this.priority = priority
        }

        constructor(prefix: String) {
            this.prefix = prefix
        }

        override fun newThread(r: Runnable): Thread {
            val executor = Thread(r, prefix + " #" + counter.getAndIncrement())
            executor.isDaemon = true
            executor.priority = priority
            return executor
        }
    }

    /**
     * 抛弃当前的任务
     */
    private class CHandlerException : ThreadPoolExecutor.AbortPolicy() {
        override fun rejectedExecution(r: Runnable, e: ThreadPoolExecutor) {
            Log.d(TAG, "rejectedExecution:$r")
            Log.e(TAG, logAllThreadStackTrace().toString())
            //            Tips.showForce("任务被拒绝", 5000);
            if (!pool!!.isShutdown) {
                pool!!.shutdown()
                pool = null
            }
            pool = createThreadPoolExecutor()
        }
    }

}